package co.gov.medellin.sdep.sisdep.domain;

import java.io.File;
import java.io.Serializable;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Iterator;
import java.util.List;

import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;
import javax.faces.model.SelectItem;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.fileupload.FileItem;
import org.apache.commons.fileupload.FileItemFactory;
import org.apache.commons.fileupload.disk.DiskFileItemFactory;
import org.apache.commons.fileupload.servlet.ServletFileUpload;

import co.gov.medellin.sdep.sisdep.admin.exception.ValueObjectException;
import co.gov.medellin.sdep.sisdep.admin.service.IValueObjectService;
import co.gov.medellin.sdep.sisdep.cache.MasterListCache;
import co.gov.medellin.sdep.sisdep.common.AppConstants;
import co.gov.medellin.sdep.sisdep.common.OracleHelper;
import co.gov.medellin.sdep.sisdep.common.dto.GenericComponentVO;
import co.gov.medellin.sdep.sisdep.common.dto.GenericComponentValueVO;
import co.gov.medellin.sdep.sisdep.common.exception.ArchivoAnexoException;
import co.gov.medellin.sdep.sisdep.common.service.IArchivoAnexoService;
import co.gov.medellin.sdep.sisdep.model.ArchivoAnexo;

import com.sap.security.api.IUser;
import com.sap.security.api.UMFactory;


/**
 * <p>
 * Clase utilizada para la carga de los diferentes tipos de archivos que se almacenan en SISDEP.
 * </p>
 * <p>
 * Copyright (c) 2012<BR>
 * Consult-Soft S.A.<BR>
 * Created on: May 22, 2012<BR>
 * 
 * @author atabares
 * </p>
 */
public class UploadFileController extends BaseController implements Serializable {

	/** Serial key. */
	private static final long serialVersionUID = -513060861114082318L;

	/** Instancia de servicio archivoAnexoService. */
	private IArchivoAnexoService archivoAnexoService;
	
	/** Instancia de servicio valueObjectService. */
	private IValueObjectService valueObjectService;
	
	/** Objeto que contiene la lista de los valores asociados al tipo SDEPPathAreaType. */
	private GenericComponentVO genericComponentVO = null;
	
	/** Nombre del tipo de archivo cargado. Este parametro define la ubicacion del archivo en el server. */
	private String loadedFileType;
		
	/** Objeto contenedor del listado de Archivos asociados con un elemento. */
	private transient DataModel fileListModel;

	/** Objeto contenedor de la informacion del usuario autenticado en el Portal SAP. */
	private IUser userPortal;
	
	/**
	 * Constructor por defecto.
	 */
	public UploadFileController() {
		this.fileListModel = new ListDataModel();
	}

	/**
	* Obtiene el parametro archivoAnexoService.
	* @see UploadFileController#archivoAnexoService
	*
	* @return El parametro archivoAnexoService.
	*/
	public IArchivoAnexoService getArchivoAnexoService() {
		return archivoAnexoService;
	}

	/**
	* Asigna el parametro archivoAnexoService.
	* @see UploadFileController#archivoAnexoService
	*
	* @param archivoAnexoService valor a asignar al parametro archivoAnexoService.
	*/
	public void setArchivoAnexoService(IArchivoAnexoService archivoAnexoService) {
		this.archivoAnexoService = archivoAnexoService;
	}

	/**
	* Obtiene el parametro valueObjectService.
	* @see UploadFileController#valueObjectService
	*
	* @return El parametro valueObjectService.
	*/
	public IValueObjectService getValueObjectService() {
		return valueObjectService;
	}

	/**
	* Asigna el parametro valueObjectService.
	* @see UploadFileController#valueObjectService
	*
	* @param valueObjectService valor a asignar al parametro valueObjectService.
	*/
	public void setValueObjectService(IValueObjectService valueObjectService) {
		this.valueObjectService = valueObjectService;
	}

	/**
	* Obtiene el parametro loadedFileType.
	* @see UploadFileController#loadedFileType
	*
	* @return El parametro loadedFileType.
	*/
	public String getLoadedFileType() {
		return loadedFileType;
	}

	/**
	* Asigna el parametro loadedFileType.
	* @see UploadFileController#loadedFileType
	*
	* @param loadedFileType valor a asignar al parametro loadedFileType.
	*/
	public void setLoadedFileType(String loadedFileType) {
		this.loadedFileType = loadedFileType;
	}

	/**
	 * Metodo que permite iniciar el proceso de carga de un archivo y su copia al servidor SISDEP.
	 * 
	 * @param request
	 * @return La cadena que redirecciona a la pagina de creacion/actualizacion de disenios de modulos.
	 * @throws Throwable
	 */
	public String uploadFileToLocalDirectory(HttpServletRequest request) throws Throwable {
		
		ArchivoAnexo archivoAnexoSession = (ArchivoAnexo) UploadFileController.getSessionObject(AppConstants.SESSION_PENDING_FILE);
		
		if ((archivoAnexoSession != null) && (archivoAnexoSession.getIdTipoAnexo() != null)) {
			// FileItemFactory es la interfaz para crear el FileItem.
			FileItemFactory fileItemFactory = new DiskFileItemFactory();
			// ServletFileUpload convierte los input file a FileItem.
			ServletFileUpload servletFileUpload = new ServletFileUpload(fileItemFactory);
			
			// Se obtiene el FileItem del ServletFileUpload en una lista.
			List items = servletFileUpload.parseRequest(request);
	
			for(int i=0; i<items.size(); i++){
				// El FileItem representa el archivo en memoria que va a ser cargado.
			    FileItem item = (FileItem) items.get(i);
				
			    // item.isFormField() ? false=input file; true=text field.
			    if (!item.isFormField()){
					archivoAnexoSession.setNombreInterno(this.generateFileName(archivoAnexoSession, item.getName()));
					archivoAnexoSession.setNombreOriginal(item.getName().substring(item.getName().lastIndexOf("\\") + 1));
					archivoAnexoSession.setMimetype(item.getContentType());
					
			    	File archivoServer = new File(archivoAnexoSession.getServerPath() + archivoAnexoSession.getNombreInterno());
					
					if (archivoAnexoSession.isActualizacion() && archivoAnexoSession.getId() != null) {
						try {
							// Se obtiene el archivo anterior asociado.
							ArchivoAnexo archivoAnexoUpdate = getArchivoAnexoService()
									.get(archivoAnexoSession.getId().longValue());
							
							// Se asocia el ID del archivo anterior al nuevo archivo para que sea actualizado.
							archivoAnexoSession.setId(archivoAnexoUpdate.getId());
							
							if (this.deleteFile(archivoAnexoUpdate)) {
								item.write(archivoServer);
								
								getArchivoAnexoService().update(archivoAnexoSession);
							} else {
								return "Error al eliminar el Archivo antiguo!";
							}
						} catch (Exception e) {
							e.printStackTrace(System.err);
							
							return "Error en la carga de Archivo!";
						}
					} else {
						try {
							item.write(archivoServer);
							getArchivoAnexoService().insert(archivoAnexoSession);
							archivoAnexoSession.setId(getArchivoAnexoService().getCurrentInsert());
							/*
							KMRepositoryManager.uploadFileKM(userPortal, "/AlcaldiaMedellin/BienestarSocial/sibis/"
									  /*archivoAnexoSession.getServerPath(), item.get(), archivoAnexoSession.getNombreInterno());*/
						} catch (Exception e) {
							e.printStackTrace(System.err);
							
							return "Error en la carga de Archivo!";
						}
					}
			    }
			}
			
			this.setSessionObject(AppConstants.SESSION_PENDING_FILE, null);
			this.setSessionObject(AppConstants.SESSION_UPLOADED_FILE, archivoAnexoSession);
			
			return "Archivo cargado con exito!";
		} else {
			this.setServerStoragePath(archivoAnexoSession);
		}
		
		this.setSessionObject(AppConstants.SESSION_PENDING_FILE, archivoAnexoSession);
	    
	    return AppConstants.EMPTY;
	}
	
	/**
	 * Metodo que permite inicializar el listado de items de archivos anexos asociados en el elemento
	 * actual para carga de archvos.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 24-07-2012
	 * 
	 */
	public void initCurrentFileList() {
		ArchivoAnexo archivoAnexoSession = (ArchivoAnexo) UploadFileController.getSessionObject(AppConstants.SESSION_PENDING_FILE);
		if(null == archivoAnexoSession) {
			archivoAnexoSession = (ArchivoAnexo) UploadFileController.getSessionObject(AppConstants.SESSION_UPLOADED_FILE);
		}
		try {
			if ((archivoAnexoSession != null) && (archivoAnexoSession.getIdTipoAnexo() != null)) {
				getFileListModel().setWrappedData(getArchivoAnexoService().loadByCriteria(archivoAnexoSession));
			}
		} catch (Exception e) {
			e.printStackTrace(System.err);
			
			((MessageModalWindowController) getBean(AppConstants.MESSAGE_CONTROLLER_BEAN))
				.setErrorMessage(AppConstants.DEFAULT_ERROR + e.getMessage(), null, null);
		}			
	}
	
	/**
	 * Metodo que permite inicializar el objeto que representa al usuario autenticado actualmente en el Portal SAP.
	 * 
	 * @author carlos.perez@consult-soft.com
	 * @version 25-07-2012
	 * 
	 */
	public void initUserPortal(HttpServletRequest request, HttpServletResponse response) {
		if (null == userPortal) {
			userPortal = UMFactory.getAuthenticator().getLoggedInUser(request, response);
		}
	}
	
	/**
	 * Metodo que permite generar una instancia dinamicamente del UploadFileController sin
	 * tener que usar una Expresion EL para inicializarlo en una JSF.
	 * 
	 * @return UploadFileController Instancia de UploadFileController.
	 */
	public final static UploadFileController getInstance() {
		return (UploadFileController) FacesController.getBeanInstance("uploadFileController");
	}
	
	/**
	 * Metodo que permite generar un nombre unico para el archivo que se guardara en el servidor.
	 * @param archivoAnexo Archivo a cargar.
	 * @param userFileName Nombre del archivo cargado por el usuario, es necesario para conocer la extension
	 * o tipo de archivo.
	 * @return String Nombre que llevara el archivo con su extension.
	 */
	private static String generateFileName(ArchivoAnexo archivoAnexo, String userFileName) {
		SimpleDateFormat dateFormat = new SimpleDateFormat("ddMMyyyy");
		Date fecha = new Date();
		
		String generatedFileName = archivoAnexo.getTipoAnexo().toString();
		
		String fileNameDate = dateFormat.format(fecha);
		int fileNameHour = fecha.getHours();
		int fileNameMinute = fecha.getMinutes();
		int fileNameSecond = fecha.getSeconds();
		
		String extension = userFileName.substring(userFileName.lastIndexOf(".")).toLowerCase();
		
		generatedFileName.concat("_").concat(fileNameDate).concat("_");
		
		return generatedFileName.concat("_").concat(fileNameDate).concat("_")
				.concat(String.valueOf(fileNameHour)).concat(String.valueOf(fileNameMinute))
					.concat(String.valueOf(fileNameSecond)).concat(extension);
	}
	
	/**
	 * Metodo que prepara el proceso de carga de archivo, mediante la asignacion del ID del tipo de archivo 
	 * que se cargara, la asignacion del Path donde se almacenara el archivo y el usuario que ejecuta la carga.
	 * @param archivoAnexo Archivo a cargar.
	 * @throws ValueObjectException 
	 */
	private void setServerStoragePath(ArchivoAnexo archivoAnexo) throws ValueObjectException {
		List sdepPathAreaTypeList = ((MasterListCache) getBean("masterListCache")).getSDEPPathAreaType();
		
		Iterator iterator = sdepPathAreaTypeList.iterator();
		
		while (iterator.hasNext()) {
			SelectItem selectItem = (SelectItem) iterator.next();
			
			if (selectItem.getLabel().equals(archivoAnexo.getTipoAnexo().toString())) {
				// Se hace necesario obtener el objeto GenericComponentValueVO, ya que la lista del tipo 
				// cargada en cache no contiene la propiedad Path(Es la descripcion del valor tipo).
				GenericComponentValueVO genericComponentValueVO = 
						getValueObjectService().getTypeValue(selectItem.getValue().toString());
				
				// Asignacion de ID de tipo de anexo y server Path.
				archivoAnexo.setIdTipoAnexo(new Long (genericComponentValueVO.getComponentValueId()));
				archivoAnexo.setServerPath(genericComponentValueVO.getComponentValueDesc());
				archivoAnexo.setUsuarioCrea(this.getLoggedUserID());
				
				break;
			}
		}
	}
	
	/**
	 * Metodo que permite eliminar un archivo que se encuentra almacenado en el servidor.
	 * @param archivoAnexo Informacion del archivo que se quiere eliminar.
	 * @return String Nombre que llevara el archivo con su extension.
	 * @throws ArchivoAnexoException 
	 */
	public boolean deleteFile(ArchivoAnexo archivoAnexo) throws ArchivoAnexoException {
		ArchivoAnexo archivoAnexoDelete;
		File deleteFile;
		
		// Si no se conoce el path y el nombre del archivo, se consulta en DB.
		if (OracleHelper.isNullorEmpty(archivoAnexo.getServerPath()) 
				|| OracleHelper.isNullorEmpty(archivoAnexo.getNombreInterno())) {
			archivoAnexoDelete = getArchivoAnexoService().get(archivoAnexo.getId().longValue());
			
			deleteFile = new File(archivoAnexoDelete.getServerPath() + archivoAnexoDelete.getNombreInterno());
		} else {
			deleteFile = new File(archivoAnexo.getServerPath() + archivoAnexo.getNombreInterno());
		}
		
		if (deleteFile.delete()) {
			getArchivoAnexoService().delete(archivoAnexo);
			
			return true;
		} else {
			return false;
		}
	}

	/**
	 * @return the fileListModel
	 */
	public DataModel getFileListModel() {
		return fileListModel;
	}

	/**
	 * @param fileListModel the fileListModel to set
	 */
	public void setFileListModel(DataModel fileListModel) {
		this.fileListModel = fileListModel;
	}
}
